package br.com.arquitetura.model;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class Arquitetura {
	
	
	public static final int R0  	= Microinstrucao.R0;
	public static final int R1  	= Microinstrucao.R1;
	public static final int R2  	= Microinstrucao.R2;
	public static final int R3  	= Microinstrucao.R3;
	public static final int R4  	= Microinstrucao.R4;
	public static final int RT  	= Microinstrucao.RT;
	public static final int RDADO 	= Microinstrucao.RDADO;
	public static final int PC  	= Microinstrucao.PC;
			
	public static final int IR  	= Microinstrucao.IR;
	public static final int REND  	= Microinstrucao.REND;
	
	public static final int MEM_EN 	= Microinstrucao.MEM_EN;
	public static final int MEM_RW 	= Microinstrucao.MEM_RW;
			
	public static final int MUXA0  	= Microinstrucao.MUXA0;
	public static final int MUXA1  	= Microinstrucao.MUXA1;
	public static final int MUXA2  	= Microinstrucao.MUXA2;
			
	public static final int MUXB0  	= Microinstrucao.MUXB0;
	public static final int MUXB1  	= Microinstrucao.MUXB1;
	public static final int MUXB2  	= Microinstrucao.MUXB2;
	
	public static final int MUX  	= Microinstrucao.MUX;
	
	public static final int ULA0  	= Microinstrucao.ULA0;
	public static final int ULA1  	= Microinstrucao.ULA1;
	public static final int ULA2  	= Microinstrucao.ULA2;
	public static final int ULA3  	= Microinstrucao.ULA3;

	
	
	
	
	private Memoria memoria;
	private Map<String, Mux> muxs;
	private Map<String, Registrador> registradores;
//	private Map<String, Sinal> sinais;
	private Barramento barramento;
	private ULA ula;
	private UC uc;
	private Microinstrucao microinstrucao;
	
	
	public Arquitetura(){
		muxs = new LinkedHashMap<String, Mux>();
		registradores = new LinkedHashMap<String, Registrador>();
//		sinais = new LinkedHashMap<String, Sinal>();
		
		barramento = new Barramento(null);
		
//		iniciaSinais();		
		iniciaRegistradores();
		
		iniciaMemoria();
		
		iniciaMultiplexadores();
		
		iniciaUla();
		
		uc = new UC(registradores.get("IR"));
		
		barramento.setEntrada(ula);
		
		registradores.get("RDADO").setEntrada(muxs.get("MUX"));
	}

	private void iniciaMemoria() {
		List<Componente> entradasMemoria = new ArrayList<Componente>();
		
		entradasMemoria.add(registradores.get("REND"));
		entradasMemoria.add(registradores.get("RDADO"));
		
		memoria = new Memoria(entradasMemoria);
	}

	private void iniciaUla() {
		ula = new ULA(muxs.get("MUXA"), muxs.get("MUXB"));
	}

	private void iniciaMultiplexadores() {
		List<Componente> entradas = new ArrayList<Componente>();
		entradas.add(registradores.get("RDADO"));
		entradas.add(registradores.get("R0"));
		entradas.add(registradores.get("R1"));
		entradas.add(registradores.get("R2"));
		entradas.add(registradores.get("R3"));
		entradas.add(registradores.get("R4"));
		entradas.add(registradores.get("RT"));
		entradas.add(registradores.get("PC"));
		
		muxs.put("MUXA", new Mux(entradas));
		muxs.put("MUXB", new Mux(entradas));
		
		entradas = new ArrayList<Componente>();
		entradas.add(memoria);
		entradas.add(barramento);
		muxs.put("MUX", new Mux(entradas));
	}

	private void iniciaRegistradores() {
		registradores.put("R0", new Registrador("R0", barramento, R0));
		registradores.put("R1", new Registrador("R1", barramento, R1));
		registradores.put("R2", new Registrador("R2", barramento, R2));
		registradores.put("R3", new Registrador("R3", barramento, R3));
		registradores.put("R4", new Registrador("R4", barramento, R4));		
		registradores.put("RT", new Registrador("RT", barramento, RT));
		registradores.put("PC", new Registrador("PC", barramento, PC));
		registradores.put("IR", new Registrador("IR", barramento, IR));
		registradores.put("REND", new Registrador("REND", barramento, REND));
		
		registradores.put("RDADO", new Registrador("RDADO", null, RDADO));
		
		registradores.get("PC").setSaida(0);
	}

	
	
	public void executaMicroInstrucao() {
		int controle = microinstrucao.getControle();
		
		//prepara microinstrucao a ser executada
		
		microinstrucao = uc.getProximaMicroinstrucao();
		
		
		//T1 libera registros nas barras de entrada da ULA
		
		muxs.get("MUXA").atualizaSaida( 
				( (MUXA0|MUXA1|MUXA2) & controle) / MUXA0 );

		muxs.get("MUXB").atualizaSaida( 
				( (MUXB0|MUXB1|MUXB2) & controle) / MUXB0 );
		
		
		//T2 opera sobre valores fornecidos
		
		ula.atualizaSaida(
				( (ULA0|ULA1|ULA2|ULA3) & controle  ) / ULA0 );
		
		barramento.atualizaSaida();
		
		
		//T3 carga do resultado & [realiza operacoes da memoria]
		
		muxs.get("MUX").atualizaSaida(				
				( MUX & controle ) / MUX );
		
		for (Registrador registrador : registradores.values()) {
			
			if ( ( registrador.getSinal() & controle ) 
					== registrador.getSinal() ) {
				registrador.atualizaSaida();
			}
			
		}
		
		if ( (MEM_EN & controle) == MEM_EN ) {
			memoria.atualizaSaida(
					( (MEM_RW & controle ) / MEM_RW ) );
		}
	}
	
	
	public void executaInstrucao() {
		
		do {
			executaMicroInstrucao();
		} while ( microinstrucao.getEndereco() != MemoriaDeControle.BUSCA_INSTRUCAO
				&& microinstrucao.getEndereco() != MemoriaDeControle.FIM_DE_PROGRAMA);
		
	}
	
	
	public void executaPrograma() {
	
		do {
			executaInstrucao();
		} while ( microinstrucao.getEndereco() != MemoriaDeControle.FIM_DE_PROGRAMA );
		
	}
	
	
	
	
	
	
	
	public Memoria getMemoria() {
		return memoria;
	}
	public void setMemoria(Memoria memoria) {
		this.memoria = memoria;
	}
	public Map<String, Mux> getMuxs() {
		return muxs;
	}
	public void setMuxs(Map<String, Mux> muxs) {
		this.muxs = muxs;
	}
	public Map<String, Registrador> getRegistradores() {
		return registradores;
	}
	public void setRegistradores(Map<String, Registrador> registradores) {
		this.registradores = registradores;
	}
	public ULA getUla() {
		return ula;
	}
	public void setUla(ULA ula) {
		this.ula = ula;
	}


}
